---
layout: default
title: Input Mask Guide
---
{% include guide_header.html %}
<main>
  <h2 id="getting-started" class="section-h"><a href="#getting-started">Getting Started</a></h2>
  <p>Install from <a href="https://www.npmjs.com/package/imask">npm</a>:</p>
{% highlight shell %}
npm install imask
{% endhighlight %}
  <p>And import or require:</p>
{% highlight shell %}
import IMask from 'imask';
{% endhighlight %}
  <p>or use CDN:</p>
{% highlight html %}
<script src="https://unpkg.com/imask"></script>
{% endhighlight %}
  <p>Simple use case:</p>
  <div class="toggle-panel">
    <label for="start-phone-mask-demo">demo</label>
    <input id="start-phone-mask-demo" type="checkbox">
    <div class="toggle-panel-content">
      <form class="form">
        <div class="form-item">
          <label>Type here against mask <span class="desc">+{7}(000)000-00-00</span></label>
          <input id="start-phone-mask" type="text" value="">
          <div class="desc">unmasked: <span id="start-phone-unmasked"></span><span id="start-phone-complete" class="example__complete"></span></div>
        </div>
      </form>
    </div>
  </div>
{% highlight javascript %}
const element = document.getElementById('selector');
const maskOptions = {
  mask: '+{7}(000)000-00-00'
};
const mask = IMask(element, maskOptions);
{% endhighlight %}
  
  <script async type="text/javascript" src="//cdn.carbonads.com/carbon.js?serve=CEAI6KQE&placement=imaskjsorg" id="_carbonads_js"></script>
      <link rel="stylesheet" href="css/carbon-guide.css">

  <fieldset>
    <strong>Please note</strong>
    <p>If you apply mask to <code>input</code> element you have to use <code>type="text"</code>. Other types are not supported.</p>
  </fieldset>

  <p>IMask consists of two independent layers: model and view.<br>
  Model layer contains all masking facilities which can be used independently without UI.</p>
  <p id="support-outdated">View layer is a glue between UI element and model, it connects listeners and controls changes in both directions.</p>
  <p>Input processing is based on a simple idea of comparing states before and after change. State before change is obtained on <code>keydown</code> and on <code>input</code> actual processing takes place. <ins>In order to support older browsers</ins> manually call <code>_saveSelection</code> to save state and <code>_onInput</code> to handle changes. Pull requests for the beauty are welcomed.</p>
  <p>Currently, view layer contains only one component <code>InputMask</code> which supports HTML input-like API. Instance of <code>InputMask</code> is returned when IMask constructor is called.</p>
  <p>To create new mask on element use:</p>
{% highlight javascript %}
const mask = IMask(element, maskOptions);
{% endhighlight %}

  <p>Get/set value and unmasked value:</p>
{% highlight javascript %}
mask.value = '+7(999)999-99-99';
console.log(mask.value);  // '+7(999)999-99-99'
console.log(mask.unmaskedValue);  // '79999999999'

mask.unmaskedValue = '70000000000';
console.log(mask.value);  // '+7(000)000-00-00'
console.log(mask.unmaskedValue);  // '70000000000'
{% endhighlight %}

  <p>For typed masks like <code>Number</code> or <code>Date</code> it is possible to work with typed values:</p>
{% highlight javascript %}mask.updateOptions({mask: Number});
mask.typedValue = 100;  // use number
console.log(mask.value);  // '100'
console.log(mask.unmaskedValue);  // '100'
console.log(mask.typedValue);  // 100
{% endhighlight %}
  <p>For untyped masks <code>typedValue</code> and <code>unmaskedValue</code> works identically.</p>

  <p>Update options:</p>
{% highlight javascript %}
mask.updateOptions({
  mask: Number,
  min: 0,
  max: 100
});  // also updates UI
{% endhighlight %}

  <p>Clean and destroy:</p>
{% highlight javascript %}
mask.destroy();
{% endhighlight %}

  <p>Listen to events:</p>
{% highlight javascript %}
const log = () => console.log(mask.value);
// 'accept' event fired on input when mask value has changed
mask.on('accept', log);

// 'complete' event fired when the value is completely filled
// Note: this makes sense only for Pattern-based masks
mask.on('complete', () => console.log(mask.value));
{% endhighlight %}

  <p>Stop listening to events:</p>
{% highlight javascript %}
mask.off('accept', log);

// omit handler argument to unbind all handlers
mask.off('complete');
{% endhighlight %}

  <p>Get <code>Masked</code> model:</p>
{% highlight javascript %}
const masked = mask.masked;
masked.reset(); // UI will NOT be updated
{% endhighlight %}

  <p>In the above example all changes are proxied to the model layer first and then UI is updated. The core of masking on model layer is <a href="#masked-base">IMask.Masked</a> base class.
  There are also several other model classes for the different <code>mask</code> property types that provide additional functionality:</p>
  <table>
    <thead>
      <tr>
        <th><code>mask</code> prop</th>
        <th>Model class</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td><code>IMask.Masked</code> descendant or instance</td>
        <td><a href="#masked-base">IMask.Masked</a></td>
      </tr>
      <tr>
        <td><code>RegExp</code></td>
        <td><a href="#masked-base">IMask.MaskedRegExp</a></td>
      </tr>
      <tr>
        <td><code>Function</code></td>
        <td><a href="#masked-base">IMask.MaskedFunction</a></td>
      </tr>
      <tr>
        <td><code>String</code></td>
        <td><a href="#masked-pattern">IMask.MaskedPattern</a></td>
      </tr>
      <tr>
        <td><code>Number</code></td>
        <td><a href="#masked-number">IMask.MaskedNumber</a></td>
      </tr>
      <tr>
        <td><code>Date</code></td>
        <td><a href="#masked-date">IMask.MaskedDate</a></td>
      </tr>
      <tr>
        <td><code>Array</code> of masks</td>
        <td><a href="#masked-dynamic">IMask.MaskedDynamic</a></td>
      </tr>
    </tbody>
  </table>

  <p>Mask also can be used without UI, e.g.</p>
{% highlight javascript %}
const masked = IMask.createMask({
  mask: '+7 (000) 000-00-00',
  // ...and other options
});
masked.resolve('71234567890');

// now you can access masked value
console.log(masked.value);
// and get unmasked value
console.log(masked.unmaskedValue);
{% endhighlight %}

  <h2 id="masked" class="section-h"><a href="#masked">Masked</a></h2>
  <nav class="breadcrumbs push-center">
    <ul>
      <li><a href="#masked-base">base</a></li>
      <li><a href="#masked-number">number</a></li>
      <li><a href="#masked-pattern">pattern</a></li>
      <li><a href="#masked-range">range</a></li>
      <li><a href="#masked-enum">enum</a></li>
      <li><a href="#masked-date">date</a></li>
      <li><a href="#masked-dynamic">dynamic</a></li>
    </ul>
  </nav>


  <h3 id="masked-base" class="section-h"><a href="#masked-base">Common Behavior</a></h3>
  <p><a href="#masked-base">IMask.Masked</a> is a base class of all other <code>Masked*</code> classes. Almost all base class options are applicable to subclasses.</p>
  <p>Example usage:</p>
  <div class="toggle-panel">
    <label for="validate-mask-demo">demo</label>
    <input id="validate-mask-demo" type="checkbox">
    <div class="toggle-panel-content">
      <form class="form">
        <div class="form-item">
          <label>Type here against RegExp mask <span class="desc">/^\d+$/</span></label>
          <input id="validate-mask" type="text" value="">
        </div>
      </form>
    </div>
  </div>
{% highlight javascript %}
const digitsMask = IMask(element, {
  mask: /^\d+$/
});
{% endhighlight %}

  <p>Update options:</p>
{% highlight javascript %}
mask.updateOptions({
  // while changing mask only same type allowed
  mask: /^\w+$/,  // ok
  // mask: "0000",  // ERROR! changing mask type on existing mask is not allowed!

  // ... other options
});
{% endhighlight %}

  <p>Get/set value and unmasked value:</p>
{% highlight javascript %}
masked.value = 'hello world!';
console.log(masked.unmaskedValue);
// or typed value if it makes sense
console.log(masked.typedValue);
{% endhighlight %}

  <p>Use <code>prepare (value, masked)</code> or <code>prepareChar (value, masked)</code> option for preprocessing input and <code>commit (value, masked)</code> option for postprocessing after UI is deactivated:</p>
  <div class="toggle-panel">
    <label for="uppercase-mask-demo">demo</label>
    <input id="uppercase-mask-demo" type="checkbox">
    <div class="toggle-panel-content">
      <form class="form">
        <div class="form-item">
          <label>Type to uppercase</label>
          <input id="uppercase-mask" type="text" value="">
        </div>
      </form>
    </div>
  </div>
{% highlight javascript %}
IMask(element, {
  mask: /^\w+$/,
  prepareChar: str => str.toUpperCase(),
  commit: (value, masked) => {
    // Don't change value manually! All changes should be done in mask!
    // This example helps to understand what is really changes, only for demo
    masked._value = value.toLowerCase();  // Don't do it
  }
})
{% endhighlight %}
  <p>The difference between <code>prepare</code> and <code>prepareChar</code> is that when inserting multiple symbols in a batch <code>prepare</code> is called once for the entire string and <code>prepareChar</code> is called for each symbol.</p>
  <p>Usually you don't need to create instances of that type manually, because it will be done by IMask internally. But you can subclass from <code>Masked</code> to add some specific behavior.</p>
  <p>Additionaly to <code>mask</code> option custom validator can be set as <code>validate (value, masked)</code> option for some complex checks on any mask types excluding <code>Function</code> and <code>RegExp</code>, because they are already validators themselves.</p>
  <p><strong>Note: do not mutate <code>Masked</code> instance inside callbacks.</strong></p>
  <fieldset>
    <p><strong>Also make sure that mask or validator works with any of intermediate states, not just final value. For example to restrict input to "123" you do:</strong></p>
{% highlight javascript %}
const masked = IMask.Masked({
  mask: /^123$/
});
{% endhighlight %}
    <p>But it does not allow to input any symbol at all, because it matches only whole string "123" and not "1" nor "12". Always take care of intermediate values at first, otherwise it might not work as expected. In complex cases it is better to use <code>Pattern</code> or <code>Function</code> masks.</p>
  </fieldset>
  <p>Example of using <code>Function</code> mask to accept any growing sequence from 0 to 9:</p>
  <div class="toggle-panel">
    <label for="sequence-mask-demo">demo</label>
    <input id="sequence-mask-demo" type="checkbox">
    <div class="toggle-panel-content">
      <form class="form">
        <div class="form-item">
          <label>Type here</label>
          <input id="sequence-mask" type="text" value="">
        </div>
      </form>
    </div>
  </div>
{% highlight javascript %}
IMask(element, {
  mask: value => /^\d*$/.test(value) &&
    value.split('').every((ch, i) => {
      const prevCh = value[i-1];
      return !prevCh || prevCh < ch;
    })
})
{% endhighlight %}

  <h4 id="overwrite" class="section-h"><a href="#overwrite">Overwrite Mode <kbd>since 5.0</kbd></a></h4>
  <p>Enables characters overwriting (replace or shift) instead of inserting.</p>
  <div class="form-item">
    <label>Simple <code>6-digits</code> mask in overwrite mode</label>
    <div class="input-select">
      <input id="overwrite-mask" type="text" value="">
      <select id="select-overwrite">
        <option value="false">Overwrite: false</option>
        <option value="true">Overwrite: true</option>
        <option value="shift">Overwrite: shift (new in v6.4.0)</option>
      </select>
    </div>

    <div class="toggle-panel">
      <label for="overwrite-mask-source">source</label>
      <input id="overwrite-mask-source" type="checkbox">
      <div class="toggle-panel-content">
{% highlight javascript %}
IMask(
  document.getElementById('overwrite-mask'),
  {
    mask: '000000',
    lazy: false,
  }
)
{% endhighlight %}
      </div>
    </div>
  </div>

  <h4 id="skipInvalid" class="section-h"><a href="#skipInvalid">Skip Invalid Mode <kbd>since 6.5.0</kbd></a></h4>
  <p>Enables skipping invalid characters (default is <i>true</i>).</p>
  <div class="form-item">
    <label>Try to insert value <code>0a1.2 3</code> to the simple digits mask</label>
    <div class="input-checkbox">
      <input id="skipInvalid-mask" type="text" value="">
      <label>
        <input type="checkbox" name="skipInvalid" id="toggle-skipInvalid-mask" checked>
        Skip&nbsp;invalid
      </label>
    </div>

    <div class="toggle-panel">
      <label for="skipInvalid-mask-source">source</label>
      <input id="skipInvalid-mask-source" type="checkbox">
      <div class="toggle-panel-content">
{% highlight javascript %}
IMask(
  document.getElementById('skipInvalid-mask'),
  {
    mask: '000000',
    skipInvalid: <CHECK>,
  }
)
{% endhighlight %}
      </div>
    </div>
  </div>

  <h3 id="masked-number" class="section-h"><a href="#masked-number">Number Mask</a></h3>
  <p>Number mask restricts input to integer or decimal numbers in many ways:</p>
  <div class="toggle-panel">
    <label for="numbers-mask-demo">demo</label>
    <input id="numbers-mask-demo" type="checkbox">
    <div class="toggle-panel-content">
      <form class="form numbers-form">
        <div class="form-item">
          <label>Type number here</label>
          <input id="numbers-mask" type="text" value="">
          <div class="desc">number: <span id="number-value"></span></div>
        </div>
        <fieldset>
          <legend>Options</legend>
          <div class="row gutters">
            <div class="col col-2">
              <div class="form-item">
                <label>Scale</label>
                <input type="number" name="scale" value="2">
              </div>
            </div>
            <div class="col col-2">
              <div class="form-item">
                <label>Radix char</label>
                <input type="text" maxlength="1" name="radix" value=",">
              </div>
            </div>
            <div class="col col-4">
              <div class="form-item">
                <label>Map to radix</label>
                <input type="text" name="mapToRadix" value=".">
              </div>
            </div>
            <div class="col col-4">
              <div class="form-item">
                <label class="checkbox"><input type="checkbox" name="padFractionalZeros">Pad fractional zeros</label>
                <label class="checkbox"><input checked type="checkbox" name="normalizeZeros">Normalize zeros</label>
                <label class="checkbox"><input checked type="checkbox" name="autofix">Autofix <kbd>since v7.4</kbd></label>
              </div>
            </div>
          </div>
          <div class="row gutters">
            <div class="col col-4">
              <div class="form-item">
                <label>Min</label>
                <input type="number" name="min" value="-10000">
              </div>
            </div>
            <div class="col col-4">
              <div class="form-item">
                <label>Max</label>
                <input type="number" name="max" value="10000">
              </div>
            </div>
            <div class="col col-4">
              <div class="form-item">
                <label>Thousands separator</label>
                <input type="text" maxlength="1" name="thousandsSeparator" value=" ">
              </div>
            </div>
          </div>
          <button name="apply" type="button">Apply</button>
        </fieldset>
      </form>
    </div>
  </div>
{% highlight javascript %}
IMask(element, {
  mask: Number,  // enable number mask

  // other options are optional with defaults below
  scale: 2,  // digits after point, 0 for integers
  thousandsSeparator: '',  // any single char
  padFractionalZeros: false,  // if true, then pads zeros at end to the length of scale
  normalizeZeros: true,  // appends or removes zeros at ends
  radix: ',',  // fractional delimiter
  mapToRadix: ['.'],  // symbols to process as radix

  // additional number interval options (e.g.)
  min: -10000,
  max: 10000,
  autofix: true,
})
{% endhighlight %}


  <h3 id="masked-pattern" class="section-h"><a href="#masked-pattern">Pattern Mask</a></h3>
  Use pattern when:
  <ul>
    <li>mask is complex or contains nested masks</li>
    <li>mask is fixed in size (optional symbols can provide some flexibility)</li>
    <li>placeholder is needed</li>
    <li>more reliability or flexibility on processing input is needed</li>
  </ul>
  <p>Pattern mask is just a string:</p>

  <div class="toggle-panel">
    <label for="pattern-nic-demo">demo</label>
    <input id="pattern-nic-demo" type="checkbox">
    <div class="toggle-panel-content">
      <form class="form pattern-nic-form">
        <div class="form-item">
          <label>Type here</label>
          <input id="pattern-nic-mask" type="text" value="">
          <div class="desc">unmasked: <span id="pattern-nic-unmasked"></span><span id="pattern-nic-complete" class="example__complete"></span></div>
        </div>
        <fieldset>
          <legend>Options</legend>
          <div class="row gutters">
            <div class="col col-4">
              <div class="form-item">
                <label>Mask</label>
                <input type="text" name="mask" value="{#}000[aaa]/NIC-`*[**]">
              </div>
            </div>
            <div class="col col-4">
              <div class="form-item">
                <label>Placeholder char</label>
                <input type="text" maxlength="1" name="ph-char" value="_">
              </div>
            </div>
            <div class="col col-4">
              <div class="form-item pattern-options-checkboxes">
                <label class="checkbox"><input type="checkbox" name="ph-show">lazy placeholder</label>
                <label class="checkbox"><input type="checkbox" name="overwrite">overwrite mode</label>
                <label class="checkbox"><input type="checkbox" name="eager">eager mode</label>
              </div>
            </div>
          </div>
        </fieldset>
        <fieldset>
          <legend>Values</legend>
           <div class="row gutters align-middle">
            <div class="col col-4">
              <div class="form-item">
                <label>Set raw value</label>
                <input type="text" name="raw" value="">
              </div>
            </div>
            <span class="desc">&nbsp;or&nbsp;</span>
            <div class="col col-4">
              <div class="form-item">
                <label>Set unmasked value</label>
                <input type="text" name="unmasked" value="">
              </div>
            </div>
          </div>
        </fieldset>
        <div class="form-item">
          <button name="apply" type="button">Apply</button>
        </div>
      </form>
    </div>
  </div>

{% highlight javascript %}
IMask(element, {
  mask: '{#}000[aaa]/NIC-`*[**]'
});

// or without UI element
IMask.PatternMasked({
  mask: '{#}000[aaa]/NIC-`*[**]'
});
{% endhighlight %}
  where definitions are:
  <ul class="unstyled">
  <li><code>0</code> - any digit</li>
  <li><code>a</code> - any letter</li>
  <li><code>*</code> - any char</li>
  <li>other chars which is not in custom definitions supposed to be <em>fixed</em></li>
  <li><code>[]</code> - make input optional</li>
  <li><code>{}</code> - include fixed part in unmasked value</li>
  <li><code>`</code> - prevent symbols shift back</li>
  </ul>
  <p>If definition character should be treated as fixed it should be escaped by <code>\\</code> (E.g. <code>\\0</code>).</p>

  <p>Additionally you can provide custom <code>definitions</code>:</p>
  <div class="toggle-panel">
    <label for="pattern-zip-mask-demo">demo</label>
    <input id="pattern-zip-mask-demo" type="checkbox">
    <div class="toggle-panel-content">
      <form class="form">
        <div class="form-item">
          <label>Type here against mask <span class="desc">#00000</span></label>
          <input id="pattern-zip-mask" type="text" value="">
          <div class="desc">unmasked: <span id="pattern-zip-unmasked"></span><span id="pattern-zip-complete" class="example__complete"></span></div>
        </div>
      </form>
    </div>
  </div>
{% highlight javascript %}
IMask(element, {
  mask: '#00000',
  definitions: {
    // <any single char>: <same type as mask (RegExp, Function, etc.)>
    // defaults are '0', 'a', '*'
    '#': /[1-6]/
  }
})
{% endhighlight %}
  <p>To configure placeholder use:</p>
  <div class="toggle-panel">
    <label for="pattern-phone-mask-demo">demo</label>
    <input id="pattern-phone-mask-demo" type="checkbox">
    <div class="toggle-panel-content">
      <form class="form">
        <div class="form-item">
          <label>Type here against mask <span class="desc">+{7}(000)000-00-00</span> with custom placeholder</label>
          <input id="pattern-phone-mask" type="text" value="">
          <div class="desc">unmasked: <span id="pattern-phone-unmasked"></span><span id="pattern-phone-complete" class="example__complete"></span></div>
        </div>
      </form>
    </div>
  </div>
{% highlight javascript %}
IMask(element, {
  mask: '+{7}(000)000-00-00',
  lazy: false,  // make placeholder always visible
  placeholderChar: '#'     // defaults to '_'
})
{% endhighlight %}
  
  <p>Secure text entry <kbd>since 6.6.0</kbd>:</p>
  <div class="toggle-panel">
    <label for="secure-text-mask-demo">demo</label>
    <input id="secure-text-mask-demo" type="checkbox">
    <div class="toggle-panel-content">
      <form class="form">
        <div class="form-item">
          <label>Type secure 6-digits code</label>
          <input id="secure-text-mask" type="text" value="">
          <div class="desc">value: <span id="secure-text-value"></span><span id="secure-text-complete" class="example__complete"></span></div>
        </div>
      </form>
    </div>
  </div>
{% highlight javascript %}
IMask(element, {
  mask: '000000',
  displayChar: '#',
  lazy: false,
  overwrite: 'shift',
})
{% endhighlight %}

  <p>For complex nested masks there is <code>blocks</code> option available:</p>
  <div class="toggle-panel">
    <label for="pattern-blocks-mask-demo">demo</label>
    <input id="pattern-blocks-mask-demo" type="checkbox">
    <div class="toggle-panel-content">
      <form class="form">
        <div class="form-item">
          <label>Type here against mask</label>
          <input id="pattern-blocks-mask" type="text" value="">
          <div class="desc">unmasked: <span id="pattern-blocks-unmasked"></span><span id="pattern-blocks-complete" class="example__complete"></span></div>
        </div>
      </form>
    </div>
  </div>
{% highlight javascript %}
IMask(element, {
  mask: 'Ple\\ase fill ye\\ar 19YY, month MM \\and v\\alue VL',
  lazy: false,  // make placeholder always visible

  blocks: {
    YY: {
      mask: '00',
    },

    MM: {
      mask: IMask.MaskedRange,
      from: 1,
      to: 12
    },

    VL: {
      mask: IMask.MaskedEnum,
      enum: ['TV', 'HD', 'VR']
    }
  }
})
{% endhighlight %}

  <h4 id="expose-block" class="section-h"><a href="#expose-block">Expose block value</a> <kbd>since 7.1.0</kbd></h4>
  <p>Block value can be exposed using the <code>expose</code> option on one of the blocks:</p>
  <div class="toggle-panel">
    <label for="price-mask-demo">demo</label>
    <input id="price-mask-demo" type="checkbox">
    <div class="toggle-panel-content">
      <form class="form">
        <div class="form-item">
          <label>Type here against mask</label>
          <input id="price-mask" type="text" value="">
          <div class="desc">value: <span id="price-value"></span> <span>type: </span><span id="price-type"></span></div>
        </div>
      </form>
    </div>
  </div>
{% highlight javascript %}
IMask(element, {
  mask: '$num',
  lazy: false,  // make placeholder always visible

  blocks: {
    num: {
      mask: Number,
      expose: true,
    },
  }
})
{% endhighlight %}

  <h4 id="lazy" class="section-h"><a href="#lazy">Lazy Mode</a></h4>
  <div class="form-item">
    <label></label>
    <div class="input-checkbox">
      <input id="pattern-lazy-mask" type="text" value="">
      <label>
        <input type="checkbox" name="lazy" id="toggle-pattern-lazy-mask">
        lazy
      </label>
    </div>

    <div class="toggle-panel">
      <label for="pattern-lazy-mask-source">source</label>
      <input id="pattern-lazy-mask-source" type="checkbox">
      <div class="toggle-panel-content">
{% highlight javascript %}
IMask(
  document.getElementById('pattern-lazy-mask'),
  {
    mask: Date,
    lazy: <CHECK>,
    autofix: true,
    blocks: {
      d: {mask: IMask.MaskedRange, placeholderChar: 'd', from: 1, to: 31, maxLength: 2},
      m: {mask: IMask.MaskedRange, placeholderChar: 'm', from: 1, to: 12, maxLength: 2},
      Y: {mask: IMask.MaskedRange, placeholderChar: 'y', from: 1900, to: 2999, maxLength: 4}
    }
  }
)
{% endhighlight %}
      </div>
    </div>
  </div>

  <h4 id="eager" class="section-h"><a href="#eager">Eager Mode <kbd>since 6.3.0</kbd></a></h4>
  <div class="form-item">
    <label></label>
    <div class="input-select">
      <input id="pattern-eager-mask" type="text" value="">
      <select id="select-pattern-eager-mask">
        <option value="true">true</option>
        <option value="false">false</option>
        <option value="append">"append" (since 6.5.0)</option>
        <option value="remove">"remove" (since 6.5.0)</option>
      </select>
    </div>

    <div class="toggle-panel">
      <label for="pattern-eager-mask-source">source</label>
      <input id="pattern-eager-mask-source" type="checkbox">
      <div class="toggle-panel-content">
{% highlight javascript %}
IMask(
  document.getElementById('pattern-eager-mask'),
  {
    mask: '00##00##',
    eager: <SELECT>,
  }
)
{% endhighlight %}
      </div>
    </div>
  </div>


  <h3 id="masked-range" class="section-h"><a href="#range">Range Mask</a></h3>
  <p>Range mask extends <a href="#masked-pattern">Pattern</a> mask and can be used to restrict input in a number range.</p>
  <div class="toggle-panel">
    <label for="range-mask-demo">demo</label>
    <input id="range-mask-demo" type="checkbox">
    <div class="toggle-panel-content">
      <form class="form">
        <div class="form-item">
          <label>Type here</label>
          <input id="range-mask" type="text" value="">
          <div class="desc">value: <span id="range-value"></span></div>
        </div>
      </form>
    </div>
  </div>
{% highlight javascript %}
IMask(element, {
  mask: IMask.MaskedRange,
  from: 1,
  to: 90,
  // maxLength is optional parameter to set the length of mask. To input smaller values pad zeros at start
  maxLength: 3,
  autofix: true,  // bound value

  // pattern options can be set as well
  lazy: false
})
{% endhighlight %}
  <p>Unlike <a href="#masked-number">Number</a> mask Range mask is fixed in size, accepts only integer values but can use placeholder.</p>

  <h4 id="autofix" class="section-h"><a href="#autofix">Autofix Mode</a></h4>
  <div class="form-item">
    <label>Input accepts numbers from 0 to 15. Try to type (e.g. <code>6</code>) with different autofix modes</label>
    <div class="input-select">
      <input id="range-autofix-mask" type="text" value="">
      <select id="select-autofix">
        <option value="false">Autofix: false</option>
        <option value="true">Autofix: true</option>
        <option value="pad">Autofix: pad</option>
      </select>
    </div>

    <div class="toggle-panel">
      <label for="range-autofix-mask-source">source</label>
      <input id="range-autofix-mask-source" type="checkbox">
      <div class="toggle-panel-content">
{% highlight javascript %}
IMask(
  document.getElementById('range-autofix-mask'),
  {
    mask: IMask.MaskedRange,
    from: 0,
    to: 15,
    autofix: <SELECT>,
  }
)
{% endhighlight %}
      </div>
    </div>
  </div>

  <h4 id="repeat" class="section-h"><a href="#repeat">Repeat block (since 7.3.0)</a></h4>
  <div class="form-item">
    <label>Repeat pattern digit:</label>
    <div class="input-select">
      <input id="repeat-mask" type="text" value="">
      <select id="select-repeat">
        <option value="Infinity">Repeat: Infinity</option>
        <option value="3">Repeat: 3</option>
        <option value="[2, 4]">Repeat: [2, 4]</option>
      </select>
    </div>

    <div class="toggle-panel">
      <label for="repeat-mask-source">source</label>
      <input id="repeat-mask-source" type="checkbox">
      <div class="toggle-panel-content">
{% highlight javascript %}
IMask(
  document.getElementById('repeat-mask'),
  {
    mask: 'r',
    lazy: false,
    blocks: {
      r: {
        repeat: <SELECT>,
        mask: '0',
      }
    },
  }
)
{% endhighlight %}
      </div>
    </div>
  </div>


  <h3 id="masked-enum" class="section-h"><a href="#enum">Enum Mask</a></h3>
  <p>Enum mask extends <a href="#masked-pattern">Pattern</a> mask and can be used to restrict input within characters enum.</p>
  <div class="toggle-panel">
    <label for="enum-mask-demo">demo</label>
    <input id="enum-mask-demo" type="checkbox">
    <div class="toggle-panel-content">
      <form class="form">
        <div class="form-item">
          <label for="enum-mask">Type here<div class="desc" id="enum-months" style="margin: 0px 0px 8px"></div></label>
          <input id="enum-mask" type="text" value="">
          <div class="desc">value: <span id="enum-value"></span></div>
        </div>
      </form>
    </div>
  </div>
{% highlight javascript %}
IMask(element, {
  mask: IMask.MaskedEnum,
  enum: Array.from({ length: 12 }, (_, i) =>
    new Date(0, i).toLocaleString(window.navigator.language, { month: 'long' })
  ),

  // optionally can set match function
  matchValue: (estr, istr, matchFrom) =>
    // use case insensitive match
    IMask.MaskedEnum.DEFAULTS.matchValue(estr.toLowerCase(), istr.toLowerCase(), matchFrom),

  // pattern options can be set as well
  lazy: false
})
{% endhighlight %}


  <h3 id="masked-date" class="section-h"><a href="#masked-date">Date Mask</a></h3>
  <p>Date mask extends <a href="#masked-pattern">Pattern</a> mask with more options.</p>
  <fieldset>
    <strong>Please note</strong>
    <p>if you set <code>pattern</code> option, then you also have to provide <code>format</code> and <code>parse</code> options.</p>
    <p>Also Date mask uses independent pattern blocks so it's possible to input inexistent dates until mask is complete. When last character is inserted then input is converted to Date object and get verified. This leads to weird situations when you might have incorrect day, but not being able to insert year. It is not possible to validate Date intermediate states in general way, but you still can use <code>validate</code> callback on application side to check your specific case.</p>
  </fieldset>
  <div class="toggle-panel">
    <label for="date-mask-demo">demo</label>
    <input id="date-mask-demo" type="checkbox">
    <div class="toggle-panel-content">
      <form class="form">
        <div class="form-item">
          <label>Type here</label>
          <input id="date-mask" type="text" value="">
          <div class="desc">date: <span id="date-value"></span></div>
        </div>
      </form>
    </div>
  </div>
{% highlight javascript %}
IMask(element, {
  mask: Date,  // enable date mask

  // other options are optional
  pattern: 'Y-`m-`d',  // Pattern mask with defined blocks, default is 'd{.}`m{.}`Y'
  // you can provide your own blocks definitions, default blocks for date mask are:
  blocks: {
    d: {
      mask: IMask.MaskedRange,
      from: 1,
      to: 31,
      maxLength: 2,
    },
    m: {
      mask: IMask.MaskedRange,
      from: 1,
      to: 12,
      maxLength: 2,
    },
    Y: {
      mask: IMask.MaskedRange,
      from: 1900,
      to: 9999,
    }
  },

  // define date -> str convertion
  format: date => {
    let day = date.getDate();
    let month = date.getMonth() + 1;
    const year = date.getFullYear();

    if (day < 10) day = "0" + day;
    if (month < 10) month = "0" + month;

    return [year, month, day].join('-');
  },

  // define str -> date convertion
  parse: str => {
    const yearMonthDay = str.split('-');
    return new Date(yearMonthDay[0], yearMonthDay[1] - 1, yearMonthDay[2]);
  },

  // optional interval options
  min: new Date(2000, 0, 1),  // defaults to `1900-01-01`
  max: new Date(2020, 0, 1),  // defaults to `9999-01-01`

  autofix: true,  // defaults to `false`

  // pattern options can be set as well
  lazy: false,

  // and other common options
  overwrite: true  // defaults to `false`
})
{% endhighlight %}
  <p>It is easy to use it with <a href="https://momentjs.com/">Moment.js</a>:</p>
  <div class="toggle-panel">
    <label for="moment-mask-demo">demo</label>
    <input id="moment-mask-demo" type="checkbox">
    <div class="toggle-panel-content">
      <form class="form">
        <div class="form-item">
          <label>Type here</label>
          <input id="moment-mask" type="text" value="">
          <div class="desc">date: <span id="moment-value"></span></div>
        </div>
      </form>
    </div>
  </div>
{% highlight javascript %}
const momentFormat = 'YYYY/MM/DD HH:mm';

IMask(element, {
  mask: Date,
  pattern: momentFormat,
  lazy: false,
  min: new Date(1970, 0, 1),
  max: new Date(2030, 0, 1),

  format: date => moment(date).format(momentFormat),
  parse: str => moment(str, momentFormat),

  blocks: {
    YYYY: {
      mask: IMask.MaskedRange,
      from: 1970,
      to: 2030
    },
    MM: {
      mask: IMask.MaskedRange,
      from: 1,
      to: 12
    },
    DD: {
      mask: IMask.MaskedRange,
      from: 1,
      to: 31
    },
    HH: {
      mask: IMask.MaskedRange,
      from: 0,
      to: 23
    },
    mm: {
      mask: IMask.MaskedRange,
      from: 0,
      to: 59
    }
  }
});
{% endhighlight %}


  <h3 id="masked-dynamic" class="section-h"><a href="#masked-dynamic">Dynamic Mask</a></h3>
  <p>Dynamic mask automatically selects appropriate mask from provided array of masks. Mask with the largest number of fitting characters is selected considering provided masks order.</p>
  <div class="toggle-panel">
    <label for="dynamic-mask-demo">demo</label>
    <input id="dynamic-mask-demo" type="checkbox">
    <div class="toggle-panel-content">
      <form class="form dynamic-form">
        <div class="form-item">
          <label>Type color here <span class="desc">#HEX</span> or <span class="desc">RRR,GGG,BBB</span></label>
          <div class="color-input">
            <input id="dynamic-mask" type="text" value="#ff0">
            <span id="color-box"></span>
          </div>
        </div>
      </form>
    </div>
  </div>
{% highlight javascript %}
IMask(element, {
  mask: [
    {
      mask: 'RGB,RGB,RGB',
      blocks: {
        RGB: {
          mask: IMask.MaskedRange,
          from: 0,
          to: 255
        }
      }
    },
    {
      mask: /^#[0-9a-f]{0,6}$/i
    }
  ]
})
{% endhighlight %}
  <p>It is also possible to select mask manually via <code>dispatch</code> option:</p>
  <div class="toggle-panel">
      <label for="dispatch-mask-demo">demo</label>
      <input id="dispatch-mask-demo" type="checkbox">
      <div class="toggle-panel-content">
        <form class="form dispatch-form">
          <div class="form-item">
            <label>Type phone number here <span class="desc">Greece, India, Russia codes are recognized</span></label>
            <input id="dispatch-mask" type="text" value="">
            <div class="desc">country: <span id="dispatch-value"></span></div>
          </div>
        </form>
      </div>
    </div>
{% highlight javascript %}
IMask(element, {
  mask: [
    {
      mask: '+00 {21} 0 000 0000',
      startsWith: '30',
      lazy: false,
      country: 'Greece'
    },
    {
      mask: '+0 000 000-00-00',
      startsWith: '7',
      lazy: false,
      country: 'Russia'
    },
    {
      mask: '+00-0000-000000',
      startsWith: '91',
      lazy: false,
      country: 'India'
    },
    {
      mask: '0000000000000',
      startsWith: '',
      country: 'unknown'
    }
  ],
  dispatch: (appended, dynamicMasked) => {
    const number = (dynamicMasked.value + appended).replace(/\D/g,'');

    return dynamicMasked.compiledMasks.find(m => number.indexOf(m.startsWith) === 0);
  }
})
{% endhighlight %}

  <h4 id="expose-dynamic" class="section-h"><a href="#expose-dynamic">Expose mask value</a> <kbd>since 7.1.0</kbd></h4>
  <p>Nested mask value can be exposed using the <code>expose</code> option on one of the masks:</p>
  <div class="toggle-panel">
    <label for="percent-mask-demo">demo</label>
    <input id="percent-mask-demo" type="checkbox">
    <div class="toggle-panel-content">
      <form class="form">
        <div class="form-item">
          <label>Type here against mask</label>
          <input id="percent-mask" type="text" value="">
          <div class="desc">value: <span id="percent-value"></span> <span>type: </span><span id="percent-type"></span></div>
        </div>
      </form>
    </div>
  </div>
{% highlight javascript %}
IMask(element, {
  mask: [
    { mask: '' },
    {
      mask: 'd %',
      lazy: false,
      expose: true,
      blocks: {
        d: {
          mask: Number,
          expose: true,
        },
      },
    },
  ],
})
{% endhighlight %}


  <h2 id="pipe" class="section-h"><a href="#pipe">Pipe <kbd>since 6.0</kbd></a></h2>
  <p>Since v6.0.0 <code>IMask</code> can be used for formatting or converting values through mask with <code>pipe</code> and <code>createPipe</code> routines:</p>
{% highlight javascript %}
const numberPipe = IMask.createPipe({
  mask: Number,
  scale: 2,
  thousandsSeparator: ' ',
  normalizeZeros: true,
  padFractionalZeros: true,
});
// `numberPipe` is just a function, call it to format values
numberPipe('1'); // "1,00"

// if `numberPipe` will not be reused, then just use `IMask.pipe` inplace:
IMask.pipe('1', {
  mask: Number,
  scale: 2,
  thousandsSeparator: ' ',
  normalizeZeros: true,
  padFractionalZeros: true,
}); // "1,00"
{% endhighlight %}
  <p>By default <code>pipe</code> expects masked value on input and produces masked input as well. This behavior is exactly the same as if a value was pasted in input.</p>
  <p>Pipe also provides a way to customize source and destination types:</p>
{% highlight javascript %}
// pipe(value, mask|masked, sourcetype, destinationtype);
// PIPE_TYPE = {TYPED, MASKED, UNMASKED}
// converts formated string to number
IMask.pipe(
  '1,01',
  {
    mask: Number,
    scale: 2,
    thousandsSeparator: ' ',
    normalizeZeros: true,
    padFractionalZeros: true,
  },
  IMask.PIPE_TYPE.MASKED,
  IMask.PIPE_TYPE.TYPED
); // 1.01 of number type
{% endhighlight %}
  <fieldset>
    <strong>Note:</strong> it is possible to share single <code>Masked</code> instance both for input handling and formatting with <code>pipe</code>. Just pass <code>Masked</code> to <code>pipe</code> instead of mask options. In that case <code>pipe</code> will not create or clone <code>Masked</code> instance, <code>Masked</code> state also will not be changed during formatting.
  </fieldset>
  <p><a href="https://github.com/uNmAnNeR/imaskjs/tree/master/packages/angular-imask">Angular plugin</a> provides <code>IMaskPipe</code> for convinience.</p>


  <h2 id="advanced" class="section-h"><a href="#advanced">Advanced</a></h2>
  <h3 id="treeshaking" class="section-h"><a href="#treeshaking">Treeshaking <kbd>since 6.0</kbd></a></h3>
  <p><code>IMask</code> contains a lot of cool features, which makes it pretty big in size. But you might not need all features and want to reduce your bundle size. Since v6.0.0 <code>IMask</code> provides such possibility and ships chunks along side with assembled bundle. Despite mask has been already support esm modules treeshaking did not work on it because of recursive masks and therefore circular dependencies inside. So now you can import only features that you want, for instance if only number mask is used:</p>
{% highlight javascript %}
// was before
// import IMask from 'imask'; // imports all modules

// enable treeshaking
import IMask from 'imask/holder'; // imports only factory
// add needed features
import 'imask/masked/number';
// now factory can work with number masks, but not any other

// usage is same in both cases
IMask(element, { mask: Number });
{% endhighlight %}
</main>

<script defer src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>
<script defer src="https://unpkg.com/imask"></script>
<script type="text/javascript">
  document.addEventListener("DOMContentLoaded", function () {
    var startPhoneMask = IMask(document.getElementById('start-phone-mask'), {
      mask: '+{7}(000)000-00-00'
    }).on('accept', function() {
      document.getElementById('start-phone-complete').style.display = '';
      document.getElementById('start-phone-unmasked').innerHTML = startPhoneMask.unmaskedValue;
    }).on('complete', function() {
      document.getElementById('start-phone-complete').style.display = 'inline-block';
    });


    IMask(document.getElementById('validate-mask'), {
      mask: /^\d+$/  // mask is mandatory
    });


    IMask(document.getElementById('uppercase-mask'), {
      mask: /^\w+$/,
      prepareChar: function (str) {
        return str.toUpperCase();
      },
      commit: function (value, masked) {
        // Don't change value manually! All changes should be done in mask!
        // But it works and helps to understand what is really changed
        masked._value = value.toLowerCase();
      }
    });


    IMask(document.getElementById('sequence-mask'), {
      mask: function (value) {
        return /^\d*$/.test(value) &&
          value.split('').every(function(ch, i) {
            var prevCh = value[i-1];
            return !prevCh || prevCh < ch;
          });
      }
    });


    var customMask = IMask(document.getElementById('pattern-nic-mask'), {
        mask: '{#}000[aaa]/NIC-`*[**]',
        lazy: false
    });
    customMask.on('accept', function() {
      document.getElementById('pattern-nic-complete').style.display = '';
      document.getElementById('pattern-nic-unmasked').innerHTML = customMask.unmaskedValue;
    }).on('complete', function() {
      document.getElementById('pattern-nic-complete').style.display = 'inline-block';
    });

    var customEl = document.getElementsByClassName('pattern-nic-form')[0];
    customEl.querySelector('[name=apply]').addEventListener('click', function () {
      var mask = customEl.querySelector('[name=mask]').value;

      var lazy = customEl.querySelector('[name=ph-show]').checked;
      var overwrite = customEl.querySelector('[name=overwrite]').checked;
      var eager = customEl.querySelector('[name=eager]').checked;
      var placeholderChar = customEl.querySelector('[name=ph-char]').value || '_';

      var value = customEl.querySelector('[name=raw]').value;
      customEl.querySelector('[name=raw]').value = '';

      var unmaskedValue = customEl.querySelector('[name=unmasked]').value;
      customEl.querySelector('[name=unmasked]').value = '';

      customMask.updateOptions({
        mask: mask,
        lazy: lazy,
        overwrite: overwrite,
        eager: eager,
        placeholderChar: placeholderChar
      });
      if (value) customMask.value = value;
      if (unmaskedValue) customMask.unmaskedValue = unmaskedValue;
    });


    var patternZipMask = IMask(document.getElementById('pattern-zip-mask'), {
      mask: '#00000',
      definitions: {
        // <any single char>: <same type as mask (RegExp, Function, etc.)>
        // defaults are '0', 'a', '*'
        '#': /[1-6]/
      }
    }).on('accept', function() {
      document.getElementById('pattern-zip-complete').style.display = '';
      document.getElementById('pattern-zip-unmasked').innerHTML = patternZipMask.unmaskedValue;
    }).on('complete', function() {
      document.getElementById('pattern-zip-complete').style.display = 'inline-block';
    });


    var patternPhoneMask = IMask(document.getElementById('pattern-phone-mask'), {
      mask: '+{7}(000)000-00-00',
      lazy: false,  // make placeholder always visible
      placeholderChar: '#'     // defaults to '_'
    }).on('accept', function() {
      document.getElementById('pattern-phone-complete').style.display = '';
      document.getElementById('pattern-phone-unmasked').innerHTML = patternPhoneMask.unmaskedValue;
    }).on('complete', function() {
      document.getElementById('pattern-phone-complete').style.display = 'inline-block';
    });

    var secureTextMask = IMask(document.getElementById('secure-text-mask'), {
      mask: '000000',
      displayChar: '#',
      lazy: false,
      overwrite: 'shift',
    }).on('accept', function() {
      document.getElementById('secure-text-complete').style.display = '';
      document.getElementById('secure-text-value').innerHTML = secureTextMask.value;
    }).on('complete', function() {
      document.getElementById('secure-text-complete').style.display = 'inline-block';
    });

    var patternGroupsMask = IMask(document.getElementById('pattern-blocks-mask'), {
      mask: 'Ple\\ase fill ye\\ar 19YY, month MM \\and v\\alue VL',
      lazy: false,  // make placeholder always visible

      // let define blocks
      blocks: {
        YY: {
          mask: '00'
        },

        MM: {
          mask: IMask.MaskedRange,
          from: 1,
          to: 12
        },

        VL: {
          mask: IMask.MaskedEnum,
          enum: ['TV', 'HD', 'VR']
        }
      }
    }).on('accept', function() {
      document.getElementById('pattern-blocks-complete').style.display = '';
      document.getElementById('pattern-blocks-unmasked').innerHTML = patternGroupsMask.unmaskedValue;
    }).on('complete', function() {
      document.getElementById('pattern-blocks-complete').style.display = 'inline-block';
    });


    var priceMask = IMask(document.getElementById('price-mask'), {
      mask: '$num',
      lazy: false,  // make placeholder always visible

      blocks: {
        num: {
          mask: Number,
          expose: true,
        },
      }
    }).on('accept', function() {
      document.getElementById('price-value').innerHTML = priceMask.unmaskedValue;
      document.getElementById('price-type').innerHTML = typeof priceMask.typedValue;
    });


    var rangeMask = IMask(document.getElementById('range-mask'), {
      mask: IMask.MaskedRange,
      from: 1,
      to: 90,
      maxLength: 3,
      autofix: true,

      // pattern options can be set as well
      lazy: false
    }).on('accept', function() {
      document.getElementById('range-value').innerHTML = rangeMask.value || '-';
    });

    var languages = Array.from({ length: 12 }, (_, i) =>
      new Date(0, i).toLocaleString(window.navigator.language, { month: 'long' })
    );

    document.getElementById('enum-months').innerHTML = languages.join(', ');
    var enumMask = IMask(document.getElementById('enum-mask'), {
      mask: IMask.MaskedEnum,
      enum: languages,

      // optionally can set match function
      matchValue: (estr, istr, matchFrom) =>
        // use case insensitive match
        IMask.MaskedEnum.DEFAULTS.matchValue(estr.toLowerCase(), istr.toLowerCase(), matchFrom),

      // pattern options can be set as well
      lazy: false
    }).on('accept', function() {
      document.getElementById('enum-value').innerHTML = enumMask.value || '-';
    });

    var overwriteMask = IMask(
      document.getElementById('overwrite-mask'),
      {
        mask: '000000',
        lazy: false,
      }
    );
    var selectOverwrite = document.getElementById('select-overwrite');
    selectOverwrite.addEventListener('change', function (e) {
      var val = e.target.value;
      overwriteMask.updateOptions({
        overwrite: val === 'false' ? false :
          val === 'true' ? true :
          'shift',
      });
    });

    var numberMask = IMask(document.getElementById('numbers-mask'), {
      mask: Number,
      scale: 2,
      thousandsSeparator: ' ',
      normalizeZeros: true,
      padFractionalZeros: false,
      min: -10000,
      max: 10000,
      autofix: true
    }).on('accept', function() {
      document.getElementById('number-value').innerHTML = numberMask.masked.number;
    });

    var numberEl = document.getElementsByClassName('numbers-form')[0];
    numberEl.querySelector('[name=apply]').addEventListener('click', function () {
      numberMask.updateOptions({
        scale: Number(numberEl.querySelector('[name=scale]').value),
        radix: numberEl.querySelector('[name=radix]').value,
        mapToRadix: numberEl.querySelector('[name=mapToRadix]').value.split(''),
        thousandsSeparator: numberEl.querySelector('[name=thousandsSeparator]').value,
        padFractionalZeros: numberEl.querySelector('[name=padFractionalZeros]').checked,
        autofix: numberEl.querySelector('[name=autofix]').checked,
        normalizeZeros: numberEl.querySelector('[name=normalizeZeros]').checked,
        min: numberEl.querySelector('[name=min]').value ?
          Number(numberEl.querySelector('[name=min]').value) : null,
        max: numberEl.querySelector('[name=max]').value ?
          Number(numberEl.querySelector('[name=max]').value) : null
      });
    });


    var dateMask = IMask(document.getElementById('date-mask'), {
      mask: Date,
      pattern: 'Y-`m-`d',
      lazy: false,

      format: function (date) {
        var day = date.getDate();
        var month = date.getMonth() + 1;
        var year = date.getFullYear();

        if (day < 10) day = "0" + day;
        if (month < 10) month = "0" + month;

        return [year, month, day].join('-');
      },
      parse: function (str) {
        var yearMonthDay = str.split('-');
        return new Date(yearMonthDay[0], yearMonthDay[1] - 1, yearMonthDay[2]);
      },
      min: new Date(2000, 0, 1),
      max: new Date(2020, 0, 1),
      overwrite: true,
      autofix: true
    }).on('accept', function() {
      document.getElementById('date-value').innerHTML = dateMask.masked.date || '-';
    });


    var momentFormat = 'YYYY/MM/DD HH:mm';
    var momentMask = IMask(document.getElementById('moment-mask'), {
      mask: Date,
      pattern: momentFormat,
      lazy: false,
      min: new Date(1970, 0, 1),
      max: new Date(2030, 0, 1),

      format: function (date) {
        return moment(date).format(momentFormat);
      },
      parse: function (str) {
        return moment(str, momentFormat);
      },

      blocks: {
        YYYY: {
          mask: IMask.MaskedRange,
          from: 1970,
          to: 2030
        },
        MM: {
          mask: IMask.MaskedRange,
          from: 1,
          to: 12
        },
        DD: {
          mask: IMask.MaskedRange,
          from: 1,
          to: 31
        },
        HH: {
          mask: IMask.MaskedRange,
          from: 0,
          to: 23
        },
        mm: {
          mask: IMask.MaskedRange,
          from: 0,
          to: 59
        }
      }
    }).on('accept', function() {
      document.getElementById('moment-value').innerHTML = momentMask.masked.date || '-';
    });


    var dynamicMask = IMask(
      document.getElementById('dynamic-mask'),
      {
        mask: [
          {
            mask: 'RGB,RGB,RGB',
            blocks: {
              RGB: {
                mask: IMask.MaskedRange,
                from: 0,
                to: 255
              }
            }
          },
          {
            mask: /^#[0-9a-f]{0,6}$/i
          }
        ]
      }
    ).on('accept', function() {
      var color = dynamicMask.value;
      if (!(dynamicMask.masked.currentMask.mask instanceof RegExp)) {
        color = color.split(',');
        while (color.length < 3) color.push('0');
        color = 'rgb(' + color.join(',') + ')';
      }

      document.getElementById('color-box').style.backgroundColor = color;
    });


    var dispatchMask = IMask(
      document.getElementById('dispatch-mask'),
      {
        mask: [
          {
            mask: '+00 {21} 0 000 0000',
            startsWith: '30',
            lazy: false,
            country: 'Greece'
          },
          {
            mask: '+0 000 000-00-00',
            startsWith: '7',
            lazy: false,
            country: 'Russia'
          },
          {
            mask: '+00-0000-000000',
            startsWith: '91',
            lazy: false,
            country: 'India'
          },
          {
            mask: '0000000000000',
            startsWith: '',
            country: 'unknown'
          }
        ],
        dispatch: function (appended, dynamicMasked) {
          var number = (dynamicMasked.value + appended).replace(/\D/g,'');

          return dynamicMasked.compiledMasks.find(function (m) {
            return number.indexOf(m.startsWith) === 0;
          }) || this.dynamicMasked.compiledMasks[this.dynamicMasked.compiledMasks.length-1];
        }
      }
    ).on('accept', function() {
      document.getElementById('dispatch-value').innerHTML = dispatchMask.masked.currentMask.country;
    });

    var percentMask = IMask(document.getElementById('percent-mask'), {
      mask: [
        { mask: '' },
        {
          mask: 'd %',
          lazy: false,
          expose: true,
          blocks: {
            d: {
              mask: Number,
              expose: true,
            },
          },
        },
      ],
    }).on('accept', function() {
      document.getElementById('percent-value').innerHTML = percentMask.unmaskedValue;
      document.getElementById('percent-type').innerHTML = typeof percentMask.typedValue;
    });

    var autofixMask = IMask(
      document.getElementById('range-autofix-mask'),
      {
        mask: IMask.MaskedRange,
        from: 0,
        to: 15,
        autofix: false
      }
    );
    var selectAutofix = document.getElementById('select-autofix');
    selectAutofix.addEventListener('change', function (e) {
      var val = e.target.value;
      autofixMask.updateOptions({
        autofix: val === 'false' ? false :
          val === 'true' ? true :
          'pad',
      });
    });

    var repeatMask = IMask(
      document.getElementById('repeat-mask'),
      {
        mask: 'r',
        lazy: false,
        blocks: {
          r: {
            repeat: Infinity,
            mask: '0',
          }
        },
      }
    );
    var selectRepeat = document.getElementById('select-repeat');
    selectRepeat.addEventListener('change', function (e) {
      var val = e.target.value;
      repeatMask.updateOptions({
        blocks: {
          r: {
            repeat: eval(val),
            mask: '0',
          }
        },
      });
    });

    var lazyMask = IMask(
      document.getElementById('pattern-lazy-mask'),
      {
        mask: Date,
        lazy: false,
        autofix: true,
        blocks: {
          d: {mask: IMask.MaskedRange, placeholderChar: 'd', from: 1, to: 31, maxLength: 2},
          m: {mask: IMask.MaskedRange, placeholderChar: 'm', from: 1, to: 12, maxLength: 2},
          Y: {mask: IMask.MaskedRange, placeholderChar: 'y', from: 1900, to: 2999, maxLength: 4}
        }
      }
    );

    var togglelazy = document.getElementById('toggle-pattern-lazy-mask');
    togglelazy.addEventListener('change', function (e) {
      lazyMask.updateOptions({
        lazy: e.target.checked,
      });
    });

    var eagerMask = IMask(
      document.getElementById('pattern-eager-mask'),
      {
        mask: '00##00##',
        eager: true,
      }
    );

    var selectEager = document.getElementById('select-pattern-eager-mask');
    selectEager.addEventListener('change', function (e) {
      var val = e.target.value;
      eagerMask.updateOptions({
        eager:
          val === 'false' ? false :
          val === 'true' ? true :
          val,
      });
    });

    var skipInvalidMask = IMask(
      document.getElementById('skipInvalid-mask'),
      {
        mask: '000000',
        eager: true,
      }
    );

    var toggleSkipInvalid = document.getElementById('toggle-skipInvalid-mask');
    toggleSkipInvalid.addEventListener('change', function (e) {
      skipInvalidMask.updateOptions({
        skipInvalid: e.target.checked,
      });
    });
  });
</script>
